package kevin.framework.basic.common.tree;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.beanutils.ConvertUtils;

/**
 * @ClassName: FindChildrenByPid
 * @Description: TODO
 * @author hjwen
 * @date 2016年4月30日 上午10:24:27
 * @param <T>
 * @param <K>
 */
public class FindChildrenByPid<T extends Serializable, K extends Serializable> {
	private Class<T> entityClass;
	private Class<K> idClass;
	private Method getIdMethod;
	private Method getPidMethod;

	/**
	 * @param entityClass
	 * @param idClass
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 */
	@SuppressWarnings("unchecked")
	public FindChildrenByPid(Class<T> entityClass, Class<K> idClass) throws NoSuchMethodException, SecurityException {
		this.entityClass = entityClass;
		this.idClass = idClass;
		getIdMethod = this.entityClass.getMethod("getId");
		getIdMethod.setAccessible(false);
		getPidMethod = this.entityClass.getMethod("getPid");
		getPidMethod.setAccessible(false);

	}

	/**
	 * @param pid
	 * @param source
	 * @return
	 * @throws Throwable
	 */
	@SuppressWarnings("unchecked")
	public List<T> find(K pid, List<T> source) throws Throwable {
		TreeChildResut<T> reslist = getChildren(pid, source);
		for (T obj : reslist.resultList) {
			K id = (K) ConvertUtils.convert(getIdMethod.invoke(obj), idClass);
			if (hasChildren(id, reslist.nextSource)) {
				TreeChildResut<T> result = getChildren(id, reslist.nextSource);
				for (T bean : result.resultList) {
					reslist.resultList.add(bean);
					K _id = (K) ConvertUtils.convert(getIdMethod.invoke(bean), idClass);
					if (hasChildren(_id, result.nextSource)) {
						loopChildren(reslist.resultList, _id, result.nextSource);
					}
				}
			}
		}
		return reslist.resultList;
	}

	/**
	 * 递归查找
	 ***/
	private void loopChildren(List<T> result, K _id, List<T> source) throws Throwable {
		List<T> newFinded = new ArrayList<T>();
		List<T> nextSource = new ArrayList<T>();
		for (T obj : source) {
			K pid = (K) ConvertUtils.convert(getPidMethod.invoke(obj), idClass);
			if (pid.equals(_id)) {
				result.add(obj);
				newFinded.add(obj);
			} else {
				nextSource.add(obj);
			}
		}
		if (nextSource.size() > 0) {
			for (T bean : newFinded) {
				K id = (K) ConvertUtils.convert(getIdMethod.invoke(bean), idClass);
				if (hasChildren(id, nextSource)) {
					loopChildren(result, id, nextSource);
				}
			}
		}
	}

	private TreeChildResut<T> getChildren(K pid, List<T> source) throws Throwable {
		List<T> list = new ArrayList<T>();
		List<T> nextSource = new ArrayList<T>();
		TreeChildResut<T> result = new TreeChildResut<T>();
		result.resultList = list;
		result.nextSource = nextSource;
		for (T obj : source) {
			if (pid == null || pid.equals("")) {
				// 如果是子元素并且其父元素在source 则不加入list
				if (canPutList(source, obj)) {
					list.add(obj);
				}
			} else {
				K _pid = (K) ConvertUtils.convert(getPidMethod.invoke(obj), idClass);
				if (pid.equals(_pid)) {
					list.add(obj);
				} else {
					nextSource.add(obj);
				}
			}
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	private boolean canPutList(List<T> source, T bean)
			throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		boolean put = true;
		K pid = (K) ConvertUtils.convert(getPidMethod.invoke(bean), idClass);
		for (T obj : source) {
			K id = (K) ConvertUtils.convert(getIdMethod.invoke(obj), idClass);
			if (pid.equals(id)) {
				put = false;
				break;
			}
		}
		return put;
	}

	@SuppressWarnings("unchecked")
	private boolean hasChildren(K id, List<T> source) throws Throwable {
		boolean has = false;
		for (T obj : source) {
			K pid = (K) ConvertUtils.convert(getPidMethod.invoke(obj), idClass);
			if (pid.equals(id)) {
				has = true;
				break;
			}
		}
		return has;
	}

}
